package faircat.android.exphone;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class ATSession {
	ATConnection mConn;
	ATCommand mCmd;
	Handler mHandler;
	
	RunThread mRunThread;	
	static int mState;		
	Map mRegMsg;
	
	boolean running = true;
	
	static private final int STATE_CONNECTING = 0;
	static private final int STATE_CONNECTED = 1;
	static private final int STATE_INIT = 2;
	static private final int STATE_STOPED = 3;

	static public final int AT_INIT = 0;
	static public final int AT_MSG = 1; 
	static public final int AT_FINISHED = 2;
	List<ATCommand> mATCommandList = null;
	public ATSession(ATConnection conn, Handler handler){
		mConn = conn;
		mHandler = handler;
		mCmd = null;
		mState = STATE_CONNECTING;
		Log.i("ATSession", "constructor(x,x)");
		mRegMsg = new HashMap();
	}
	
	public ATSession(){
		mCmd = null;
		mState = STATE_CONNECTING;
		Log.i("ATSession", "constructor()");
		mRegMsg = new HashMap();
		mATCommandList = Collections.synchronizedList(new LinkedList<ATCommand>());
	}
	
	public ATConnection getConn() {
		return mConn;
	}

	public void setConn(ATConnection mConn) {
		this.mConn = mConn;
	}

	public Handler getHandler() {
		return mHandler;
	}

	public void setHandler(Handler mHandler) {
		this.mHandler = mHandler;
	}

	protected void finalize(){
		Log.i("ATSession", "finalize()");
		//stop();
	}
	
	public void RegisterMsg(String line, int code){
		mRegMsg.put(line, code);
	}
	
	public void UnregisterMsg(String line){
		mRegMsg.remove(line);
	}
	
	public boolean SendCommand(ATCommand cmd){
		if((mState == STATE_CONNECTED) || (mState == STATE_INIT )){
			if(mATCommandList.isEmpty()){	
					mATCommandList.add(cmd);
					mCmd = cmd;
					try {
						mConn.write(cmd.InputNext().getBytes());
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			}else{
				mATCommandList.add(cmd);
				Log.i("ATSession", "mATCommandList.size():"+mATCommandList.size());
			}
			return true;
		}else{
			return false;
		}
		
	}
		
	public void start(){
		stop();
		running = true;
		mRunThread = new RunThread(mConn);
		mRunThread.start();
		Log.i("ATSession", "start()");
	}
		
	public void stop(){
		Log.i("ATSession", "stop()");
		mState=STATE_STOPED;
		running = false;

		mATCommandList.clear();
		if(mConn != null){
			mConn.close();
		}

	}
	
	public void ready(){
		mState = STATE_CONNECTED;
		Log.i("ATSession", "ready()");
	}
	public boolean isReady(){
		return (mState == STATE_CONNECTED);
	}
	
	private class RunThread extends Thread{
		ATConnection mConn;
		
		public RunThread(ATConnection conn){
			mConn = conn;
		}
		
		public boolean connect(){
			while(running){		
				if(!BluetoothAdapter.getDefaultAdapter().isEnabled()){
					return false;
				}
				if(isReady()){
					return false;
				}
				try{		
					mConn.connect();
					break;
				}
				catch(IOException e){
					try{
						Thread.sleep(50);
					}
					catch(InterruptedException e1){
						return false;
					}
				}
			}	
			if( !running ){
				return false;
			}

			Log.i("ATSession", "connected!");
			
			mState = STATE_INIT;
			mHandler.obtainMessage(AT_INIT, 0, 0)
				.sendToTarget();
			return true;
		}
		
		public void run(){
			Log.i("ATSession", "connecting...");
			if( !connect() ){
				return;
			}			
			//byte[] buffer = new byte[1024];
			int bytes = 0;
			String line = "";
			while(running){
				try{
					int available = mConn.available();
					if(available > 0){
						byte[] buffer = new byte[available];
						bytes = mConn.read(buffer);
						line += new String(buffer, 0, bytes);
						if( mCmd != null ){
							switch(mCmd.ParseReply(line.trim())){
							case ATCommand.RET_ERROR:
								Log.i("ATSession", line);
								line = "";
								mCmd = null;
								if(!mATCommandList.isEmpty()){
									mATCommandList.remove(0);
									if(!mATCommandList.isEmpty()){
										mCmd = mATCommandList.get(0);
										mConn.write(mCmd.InputNext().getBytes());
									}
								}
								if( mState == STATE_INIT ){						
									mHandler.obtainMessage(AT_INIT, 0, 0)
										.sendToTarget();
								}
								break;
							case ATCommand.RET_FINISHED:
								Log.i("ATSession", line);
								line = "";
								mCmd = null;
								if(!mATCommandList.isEmpty()){
									mATCommandList.remove(0);
									if(!mATCommandList.isEmpty()){
										mCmd = mATCommandList.get(0);
										mConn.write(mCmd.InputNext().getBytes());
									}
								}
								if( mState == STATE_INIT ){							
									mHandler.obtainMessage(AT_INIT, 0, 0)
										.sendToTarget();
								}
								else{
									mHandler.obtainMessage(AT_FINISHED, 0, 0)
										.sendToTarget();
								}
								break;
							case ATCommand.RET_OK:
								Log.i("ATSession", line);
								line = "";
								mConn.write(mCmd.InputNext().getBytes());
								break;
							}
						}
						else{
							Log.i("ATSession", line);
							//*
							boolean matched = false;
							Iterator<Map> iter = mRegMsg.entrySet().iterator();
							while(iter.hasNext()){
								Map.Entry entry =  (Map.Entry)iter.next();
								Pattern pattern = Pattern.compile((String)entry.getKey());
								Matcher matcher = pattern.matcher(line.trim());
								//Log.i("ATSession", "entry.getValue():"+(Integer)entry.getValue());
								if( matcher.find()){
									//Log.i("ATSession", "matcher.find():"+(Integer)entry.getValue());
									mHandler.obtainMessage(AT_MSG, (Integer)entry.getValue(), 0, matcher)
										.sendToTarget();
									matched = true;
								}
							}
							if(matched){
								line = "";
							}
							//*/
						}
					
					}					
				    else{
						if(mATCommandList.size() > 16){
					    	Log.d("ATSession", "mConn.available()="+available);
					    	Log.d("ATSession", "mATCommandList.size()="+mATCommandList.size());
							//mState = STATE_CONNECTING;
							mATCommandList.clear();
							mHandler.obtainMessage(AT_MSG, ExPhoneService.MSG_RECONNECT, 0, 0).sendToTarget();	
							break;
						}
					}
				}
				catch(IOException e){
					e.printStackTrace();
					Log.e("ATSession", "reconnecting..."+e);
					if((mState == STATE_CONNECTED) || (mState == STATE_INIT )){
						//mState = STATE_CONNECTING;
						mATCommandList.clear();
						mHandler.obtainMessage(AT_MSG, ExPhoneService.MSG_RECONNECT, 0, 0).sendToTarget();	
					}
					break;
					/*
					if( !connect() ){
						return;
					}
					continue;*/
				}
			}
			
			
		}
	}
}
